<template>
    <div :id="elemId" class="tree-select-ex" :style="{width:width}">
        <el-input
            type="text"
            v-model="showTxt"
            :placeholder="placeholder"
            clearable
            class="tree-select-ex-input"
            @focus="onDropDown"
        >
            <i v-show="loadingInit" class="el-input__icon el-icon-loading" slot="suffix" />
        </el-input>
        <div :style="{display:dropDownShow?'block':'none'}" class="tree-select-ex-dropdown">
            <el-input
                v-show="this.configParam && this.configParam.search.enable"
                ref="searchInput"
                size="small"
                type="text"
                v-model="searchTxt"
                placeholder="搜索"
                @keyup.enter.native="onSearch"
            >
                <i v-show="loadingSearch" class="el-input__icon el-icon-loading" slot="suffix" />
            </el-input>
            <div class="tree-select-ex-dropdown-tree">
                <el-tree
                    v-if="showTree"
                    :data="treeData"
                    :props="configParam.key"
                    @node-click="handleNodeClick"
                    :render-content="renderContent"
                    :default-expand-all="searchMode"
                    :expand-on-click-node="false"
                    :load="this.configParam.async.enable && !this.searchMode ? loadData : null"
                    :lazy="this.configParam.async.enable && !this.searchMode"
                ></el-tree>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'TreeSelectEx',
    props: {
        value: {
            type: [String, Number],
            default: null,
        },
        placeholder: {
            type: String,
            default: '请选择',
        },
        data: {
            type: Array,
            default: () => []
        },
        param: {
            type: Object
        },
        width: String
    },
    model: {
        prop: 'value',
        event: 'change',
    },
    created() {
        document.addEventListener("click", this.closeEvent)
        this.configParam = !this.param ? this.defaultParam : {
            key: { ...this.defaultParam.key, ...this.param.key },
            search: { ...this.defaultParam.search, ...this.param.search },
            async: { ...this.defaultParam.async, ...this.param.async },
            showMode: this.param.simple ? this.param.simple : this.defaultParam.simple,
            dataFormat: this.param.dataFormat ? this.param.dataFormat : this.defaultParam.dataFormat
        }
        this.initTree(false)
    },
    destroyed() {
        document.removeEventListener("click", this.closeEvent)
    },
    watch: {
        value(newVal, oldVal) {
            if (newVal) {
                if (this.configParam.async.queryByKey) {
                    this.loadingInit = true
                    this.configParam.async.queryByKey(newVal.toString(), data => {
                        this.showTxt = data
                        this.loadingInit = false
                    })
                }
            }
        },
        showTxt(newVal) {
            if (!newVal || newVal == '') {
                this.onClear()
            }
        }
    },
    data() {
        return {
            defaultProps: {
                children: 'children',
                label: 'label'
            },
            showTree: true,
            elemId: new Date().getTime().toString(),
            showTxt: '',
            searchTxt: '',
            treeData: [],
            buttonProps: {
                type: 'default',
                size: 'small',
            },
            configParam: null,
            defaultParam: {
                key: {
                    nodeKey: 'id',
                    parentKey: 'parentId',
                    label: 'name',
                    children: 'children',
                    isLeaf: () => true,
                },
                search: {
                    enable: false,
                    type: 'get',
                    query: (keyword, callback) => [],
                    dataFilter: data => data
                },
                async: {
                    enable: false,
                    type: 'get',
                    query: (key, callback) => [],
                    dataFilter: data => data,
                    queryByKey: null,
                },
                showMode: 'simple', // simple full
                dataFormat: 'tree', // list tree
            },
            selectedKey: null,
            dropDownShow: false,
            loadingSearch: false,
            loadingInit: false,
            searchMode: false,
        }
    },
    methods: {
        initTree(load) {
            if (this.configParam.async.enable) {
                if (load) {
                    this.loadData(null, data => {
                        data.forEach(d => d.expand = false)
                        this.treeData = data
                    })
                }
            } else {
                this.initData(this.data)
            }
        },
        initData(data) {
            if (this.configParam.dataFormat === 'list') {
                this.treeData = this.listToTree(data)
            } else if (this.configParam.dataFormat === 'tree') {
                this.treeData = data
            }
        },
        listToTree(l) {
            let result = []
            let m = new Map()
            l.forEach(d => {
                if (!m.has(d[this.configParam.key.parentKey])) {
                    m.set(d[this.configParam.key.parentKey], [])
                }
                m.get(d[this.configParam.key.parentKey]).push(d)
            })
            l.forEach(d => {
                d.children = m.get(d[this.configParam.key.nodeKey])
                m.delete(d[this.configParam.key.nodeKey])
            })
            m.forEach((v, k) => {
                v.forEach(d => result.push(d))
            })
            return result
        },
        delayDataFilter(data) {
            data.forEach(e => {
                e.expand = null
                if (this.configParam.key.isLeaf(e)) {
                    e.loading = false
                    e.children = []
                } else {
                    delete e.loading
                    delete e.children
                }
            })
            return data
        },
        loadData(node, resolve) {
            return this.configParam.async.query(node && node.data ? node.data[this.configParam.key.nodeKey] : null, data => {
                resolve(this.delayDataFilter(data))
            })
        },
        onSearch() {
            this.loadingSearch = true
            if (this.searchTxt && this.searchTxt != '') {
                this.configParam.search.query(this.searchTxt, data => {
                    this.searchMode = true
                    this.showTree = false
                    this.$nextTick(() => {
                        this.showTree = true
                        this.initData(data)
                    })
                    this.loadingSearch = false
                })
            } else {
                this.searchMode = false
                this.showTree = false
                this.$nextTick(() => {
                    this.showTree = true
                    this.initTree(false)
                })
                this.loadingSearch = false
            }
        },
        onClear() {
            this.selectedKey = null
            this.$emit('change', this.selectedKey)
        },
        renderContent(h, { node, data }) {
            return (
                <span class="custom-tree-node" onClick={
                    e => {
                        let sk = data[this.configParam.key.nodeKey]
                        if (this.selectedKey === sk && this.configParam.async.queryByKey) {
                            this.configParam.async.queryByKey(this.selectedKey.toString(), data => {
                                this.showTxt = data
                            })
                        } else {
                            this.showTxt = data[this.configParam.key.label]
                        }
                        this.selectedKey = sk
                        this.$emit('change', this.selectedKey)
                        this.onDropDownClose()
                    }
                }>
                    <span>{node.label}</span>
                </span>)
        },
        onDropDown() {
            this.dropDownShow = true
            this.$nextTick(() => {
                this.$refs.searchInput.focus()
            })
        },
        onDropDownClose() {
            this.dropDownShow = false
        },
        onToggleDropDown(state) {
            if (state) {
                this.dropDownShow = state
            } else {
                this.dropDownShow = !this.dropDownShow
            }
            if (state) {
                this.$refs.searchInput.focus()
            }
        },
        closeEvent(e) {
            const box = document.getElementById(this.elemId)
            if (box && !box.contains(e.target)) {
                this.onDropDownClose()
            }
        },
        handleNodeClick(data) {

        }
    }
}
</script>

<style>
.tree-select-ex .tree-select-ex-input input {
    cursor: pointer !important;
}
.tree-select-ex .ivu-tree-children li {
    line-height: 1.5;
}
.tree-select-ex .tree-select-ex-dropdown {
    width: 100%;
    margin: 5px 0;
    padding: 2px 5px;
    background-color: #fff;
    box-sizing: border-box;
    border-radius: 4px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
    position: absolute;
    z-index: 900;
    overflow: auto;
    padding-top: 5px;
}
.tree-select-ex .ivu-tree-empty {
    color: #bbbbbb;
    text-align: center;
}

.tree-select-ex .custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;
}
.tree-select-ex .tree-select-ex-dropdown-tree {
    margin-top: 3px;
    margin-bottom: 3px;
    min-width: 180px;
    max-height: 300px;
    overflow: auto;
}
.tree-select-ex .tree-select-ex-dropdown {
    line-height: 1;
}
.tree-select-ex .el-tree {
    min-width: 100%;
    display: inline-block !important;
}
.tree-select-ex {
    width: 100%;
}
</style>